#include "system.h"
#include "elog.h"
#include "elog_file.h"
#include "ws_response.h"
#include "ws_constant.h"
#include "dwarf_utils.h"
#include "system.pb.h"

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <time.h>
#include <sys/statfs.h>


#include <unordered_map>
#include <filesystem>

using namespace std;
namespace fs = std::filesystem;

#define KB 1024.0       // 2^10
#define MB 1048576.0    // 2^20 
#define GB 1073741824.0 // 2^30

static unordered_map<int, int (System::*)(WsPacket& ws_packet)> system_func_table {
    { CMD_SYSTEM_SET_TIME, &System::setSystemTime },
    { CMD_SYSTEM_SET_TIME_ZONE, &System::setSystemTimezone },
    { CMD_SYSTEM_SET_MTP_MODE, &System::setMTPMode },
    { CMD_SYSTEM_SET_CPU_MODE, &System::setCpuMode },
    { CMD_SYSTEM_SET_MASTER, &System::setMasterLock }
};

static TimeSetState time_set_state = TIME_NOT_SET;
static TimezoneSetState timezone_state = TIMEZONE_NOT_SET;
static MTPState mtp_state = MTP_OFF;
static SdCardInfo sdcard_info = {false, 0, 0};
static CPUMode cpu_mode = CPU_ONDEMAND;

System::System() {

}

System::~System() {

}

int System::handleMessage(WsPacket& ws_packet) {
    log_i("handle system message, cmd = %d", ws_packet.cmd());

    //在表中查找函数指针并调用
    auto it = system_func_table.find(ws_packet.cmd());
    if (it != system_func_table.end()) {
        (this->*(it->second))(ws_packet);
    }
    else {
        log_i("Function not found");
    }

    return 0;
}

void System::setTimeSetState(TimeSetState state) {
    time_set_state = state;
}

TimeSetState System::getTimeSetState() {
    return time_set_state;
}

void System::setTimezoneSetState(TimezoneSetState state) {
    timezone_state = state;
}

TimezoneSetState System::getTimezoneSetState() {
    return timezone_state;
}

int System::setSystemTime(time_t timestamp) {
    struct timeval tv;

    tv.tv_sec = timestamp;
    tv.tv_usec = 0;

    if (settimeofday(&tv, NULL) != 0) {
        log_e("Failed to set system time");
        return -CODE_SYSTEM_SET_TIME_FAILED;
    }

    setTimeSetState(TIME_SETTED);

    return 0;
}

int System::setSystemTime(WsPacket& ws_packet) {
    ReqSetTime req_set_time;
    string serialized_data = ws_packet.data();

    if (!req_set_time.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int ret = setSystemTime(req_set_time.timestamp());
    wsCommonResponse(CMD_SYSTEM_SET_TIME, MODULE_SYSTEM, CMD_TYPE_RESPONSE, ret);
    
    return 0;
}

int System::setSystemTimezone(const char* timezone_str) {
    int ret;
    FILE *fp;
    char cmd[256] = {0};
    char line[100] = {0};
    char timezone_file_path[200] = {0};

    if (getTimezoneSetState() != TIMEZONE_NOT_SET) {
        return 0;
    }

    sprintf(timezone_file_path, "/usr/share/zoneinfo/posix/%s", timezone_str);
    if((access(timezone_file_path, F_OK)) == -1) {
        log_e("no timezone file");
        return -CODE_SYSTEM_SET_TIMEZONE_FAILED;
    }

    sprintf(cmd, "ln -sf %s /etc/localtime", timezone_file_path);
    fp = popen(cmd, "r");
    if(fp != NULL) {
        while(fgets(line, sizeof(line), fp) != NULL) {
            usleep(1 * 1000);
        }
        pclose(fp);
    }
    else {
        log_e("set timezone error");
        return -CODE_SYSTEM_SET_TIMEZONE_FAILED;
    }

    struct timeval tv;
	struct tm *broken;
	struct timezone tz = {0};
	const struct timezone tz_utc = {0};

    tzset();
	gettimeofday(&tv, NULL);
	broken = localtime(&tv.tv_sec);
	tz.tz_minuteswest = timezone / 60;
	if (broken->tm_isdst > 0)
		tz.tz_minuteswest -= 60;
    printf("tz.tz_minuteswest:%d\n", tz.tz_minuteswest);
	settimeofday(NULL, &tz_utc);
	settimeofday(NULL, &tz);

    setTimezoneSetState(TIMEZONE_SETTED);

    return 0;
}
    
int System::setSystemTimezone(WsPacket& ws_packet) {
    ReqSetTimezone req_set_time_zone;
    string serialized_data = ws_packet.data();

    if (!req_set_time_zone.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int ret = setSystemTimezone(req_set_time_zone.timezone().c_str());
    wsCommonResponse(CMD_SYSTEM_SET_TIME_ZONE, MODULE_SYSTEM, CMD_TYPE_RESPONSE, ret);
    
    return 0;
}

int System::getSdCardInfo(int elapsed_time) {
    log_i("elapsed_time = %d", elapsed_time);
    struct statfs diskInfo;
    int ret = 0;
    if (checkSdExist() < 0) {
        log_e("first check tfcard not exit");
        if (!fs::exists("/dev/mmcblk2p1")) {
            log_e("second check tfcard not exit");
            sdcard_info.has_sdcard = false;
            sdcard_info.available_size = 0;
            sdcard_info.total_size = 0;

            wsResNotifySDcardInfo(CMD_NOTIFY_SDCARD_INFO, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, -WS_SDCARD_NOT_EXIST, 0, 0);

            return -WS_SDCARD_NOT_EXIST;
        }
    }

    statfs("/sdcard/", &diskInfo);
    uint64_t blocksize = diskInfo.f_bsize;                   // 每个block里包含的字节数
    uint64_t totalsize = blocksize * diskInfo.f_blocks;      // 总的字节数，f_blocks为block的数目

    uint64_t freeDisk = diskInfo.f_bfree * blocksize;       // 剩余空间的大小
    uint64_t availableDisk = diskInfo.f_bavail * blocksize; // 可用空间大小
    log_i("Disk_free = %f MB = %f GB Disk_available = %f MB = %f GB", freeDisk / MB, freeDisk / GB, availableDisk / MB, availableDisk / GB);

    sdcard_info.has_sdcard = true;
    sdcard_info.available_size = availableDisk / GB;
    sdcard_info.total_size = totalsize / GB;

    wsResNotifySDcardInfo(CMD_NOTIFY_SDCARD_INFO, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, ret, availableDisk / GB, totalsize / GB);
    
    return 0;
}

SdCardInfo System::getSdCardAvailableAndTotal() {
    return sdcard_info;
}

int System::setMTPMode(WsPacket& ws_packet) {
    if (mtp_state == MTP_OFF) {
        system("echo usb_mtp_en > /tmp/.usb_config");
		system("/etc/init.d/S50usbdevice restart");
        mtp_state = MTP_ON;
    }

    wsResNotifyMTPState(CMD_NOTIFY_MTP_STATE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, mtp_state);
    wsCommonResponse(CMD_SYSTEM_SET_MTP_MODE, MODULE_SYSTEM, CMD_TYPE_RESPONSE, 0);
    return 0;
}

MTPState System::getMTPState() {
    return mtp_state;
}

int System::getMacAddress(char* address) {
    FILE *fp;
    char wifi_address[20];
    unsigned char wifi_mac[6];
    fp = popen("cat  /sys/class/net/wlan0/address", "r");
	if (fp != NULL) {
		if (fgets(wifi_address, 20, fp) != NULL) {
            sscanf(wifi_address, "%02x:%02x:%02x:%02x:%02x:%02x", &wifi_mac[0], &wifi_mac[1], 
                            &wifi_mac[2], &wifi_mac[3], &wifi_mac[4], &wifi_mac[5]);
            
            sprintf(address, "%02X:%02X:%02X:%02X:%02X:%02X", wifi_mac[0], wifi_mac[1], wifi_mac[2], wifi_mac[3], wifi_mac[4], wifi_mac[5]);
		}
		pclose(fp);
    }

    return 0;
}

int System::getDefaultDeviceName(char* name) {
    FILE *fp;
    char wifi_address[20];
    unsigned char wifi_mac[6];
    fp = popen("cat  /sys/class/net/wlan0/address", "r");
	if (fp != NULL) {
		if (fgets(wifi_address, 20, fp) != NULL) {
            sscanf(wifi_address, "%02x:%02x:%02x:%02x:%02x:%02x", &wifi_mac[0], &wifi_mac[1], 
                            &wifi_mac[2], &wifi_mac[3], &wifi_mac[4], &wifi_mac[5]);
            sprintf(name, "DWARF_%02X%02X%02X", wifi_mac[3], wifi_mac[4], wifi_mac[5]);
        }
        pclose(fp);
    }

    return 0;
}

int System::setCpuMode(WsPacket& ws_packet) {
    ReqSetCpuMode req_set_cpu_mode;
    string serialized_data = ws_packet.data();

    if (!req_set_cpu_mode.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }

    int mode = req_set_cpu_mode.mode();

	if (mode == 0)
        system("echo ondemand > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
    else 
		system("echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");

    cpu_mode = (CPUMode)mode;
    wsResNotifyMTPState(CMD_NOTIFY_CPU_MODE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, cpu_mode);
    wsCommonResponse(CMD_SYSTEM_SET_CPU_MODE, MODULE_SYSTEM, CMD_TYPE_RESPONSE, 0);

	return 0;
}

int System::getCpuMode() {
	return cpu_mode;
}

int System::setMasterLock(WsPacket& ws_packet) {
    ReqsetMasterLock req_set_master_lock;
    string serialized_data = ws_packet.data();

    if (!req_set_master_lock.ParseFromString(serialized_data)) {
        return -WS_PARSE_PROTOBUF_ERROR;
    }


    bool master_lock = req_set_master_lock.lock();
    setWsLockMaster(master_lock);
    wsResNotifyHostSlaveMode(CMD_NOTIFY_WS_HOST_SLAVE_MODE, MODULE_NOTIFY, CMD_TYPE_NOTIFICATION, WS_HOST, ws_packet.client_id(), master_lock);

    return 0;
}